/* fpont 12/99 */
/* pont.net    */
/* udpServer.c */

/* Converted to echo client/server with select() (timeout option). See udpClient.c */
/* 3/30/05 John Schultz */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h> /* close() */
#include <string.h> /* memset() */
#include "consts.h"

#define SERVER_POR_ARG 1

typedef struct user
{
	char* userName;
	char* groups[MAX_GROUPS];
	struct sockaddr_in sock;
} user_t;

char* CurrUser = NULL;
char MsgBuff[MAX_MSG_LEN] = {0};
char* activeGroups[MAX_GROUPS] = {0};
user_t users[MAX_USERS];
struct sockaddr_in cliAddr, servAddr;

user_t * getCurrUser()
{
	int i;
	for (i = 0 ; i < MAX_USERS ; i++)
	{
		if (users[i].userName && !strcmp(users[i].userName,CurrUser))
		{
			return &users[i];
		}
	}
	return NULL;
}

int addGroupToUser(char* group)
{
	int i,index = -1;
	user_t * user = getCurrUser();
	if (!user)
		return 0;
	for (i = 0; i < MAX_GROUPS; ++i)
	{
		if (!user->groups[i])
		{
			index = i;
		}
		else if (!strncmp(user->groups[i],group,strlen(group)))
		{
			return 1;
		}
	}
	if (index < 0)
		return 0;
	user->groups[index] = (char*)malloc(sizeof(char)*(strlen(group)+1));
	strncpy(user->groups[index],group,strlen(group));
	printf("ADDDEDDEDD\n");
	return 1;
}

char* executeExit()
{
	user_t* user = getCurrUser();
	if (user)
	{
		free(user->userName);
		user->userName = NULL;
	}
	return RES_OK;
}

char* executeJoin(char* group)
{
	int i,index = -1;
	if (!group || strlen(group) < 1)
		return RES_FAIL;
	for (i = 0; i < MAX_GROUPS; ++i)
	{
		if (!activeGroups[i])
			index = i;
		else if (!strncmp(group,activeGroups[i],strlen(group)))
		{
			index = i;
			break;
		}
	}
	if (index < 0 || !addGroupToUser(group))
		return RES_FAIL;
	if (!activeGroups[index])
	{
		activeGroups[index] = (char*)malloc(sizeof(char)*(strlen(group)+1));
		strncpy(activeGroups[index],group,strlen(group));
	}
	return RES_OK;
}

char* executeGroups()
{
	memset(MsgBuff,0x0,MAX_MSG_LEN);
	int i = 0;
	for (i = 0; i < MAX_GROUPS; ++i)
	{
		char* group = activeGroups[i] ? activeGroups[i] : "";
		strncat(MsgBuff,group,strlen(group));
		if (strlen(group))
			strncat(MsgBuff," ",strlen(" "));
	}
	return MsgBuff;
}

char* safeString(char* s)
{
	return (s == NULL) ? "NULL" : s;
}

char* executeNew(char* port)
{
	int i = 0,emptySlot = -1;
	for (i = 0 ; i < MAX_USERS ; i++)
	{
		if (users[i].userName && !strcmp(users[i].userName,CurrUser))
		{
			printf("User %s already exists.\n",CurrUser);
			return RES_FAIL;
		}
		if (!users[i].userName)
			emptySlot = i;
	}
	if (emptySlot < 0)
	{
		printf("Can't add %s, queue full.\n",CurrUser);
		return RES_FAIL;
	}

	users[emptySlot].userName = (char*)malloc(sizeof(char)*(strlen(CurrUser)+1));
	strncpy(users[emptySlot].userName,CurrUser,strlen(CurrUser));

	users[emptySlot].sock.sin_family = cliAddr.sin_family;
	memcpy((char *) &users[emptySlot].sock.sin_addr,
			&cliAddr.sin_addr, sizeof(struct in_addr));
	users[emptySlot].sock.sin_port = htons(atoi(port));

	for (i = 0; i < MAX_GROUPS; ++i) {
		users[emptySlot].groups[i] = NULL;
	}
	printf("Add %s Succes.\n",CurrUser);
	return RES_OK;
}

char* executeCommand(char* cmd)
{
	char * cmd_name;
	CurrUser = strtok (cmd," ");
	cmd_name = strtok (NULL," ");
	printf("user is '%s' cmd is '%s'\n",CurrUser,cmd_name);
	if (!strcmp(cmd_name,CMD_NEW))
	{
		return executeNew(strtok (NULL, " "));
	}
	if (!strcmp(cmd_name,CMD_JOIN))
	{
		return executeJoin(strtok (NULL, " "));
	}
	else if (!strcmp(cmd_name,CMD_LEAVE))
	{
//		executeLeave(strtok (NULL, " "));
	}
	else if (!strcmp(cmd_name,CMD_EXIT))
	{
		return executeExit();
	}
	else if (!strcmp(cmd_name,CMD_MESSAGE))
	{
//		char* target = strtok (NULL, " ");
//		executeMessage(target,pch + strlen(target) + 1);
	}
	else if (!strcmp(cmd_name,CMD_GROUPS))
	{
		return executeGroups();
	}
	else if (!strcmp(cmd_name,CMD_USERS))
	{
//		char* target = strtok (NULL, " ");
//		executeUsers(target);
	}
	return ("Invalid command\n");
}

int main(int argc, char *argv[]) {

  int sd, rc, n, flags, i,j;
  unsigned int cliLen;
  char* res;
  int port;

  /* check command line args */
    if(argc!=2) {
      printf("Invalid arguments!\n");
      printf("usage  : %s <server_port>\n", argv[0]);
      exit(1);
    }
  port = atoi(argv[SERVER_POR_ARG]);

  /* socket creation */
  sd=socket(AF_INET, SOCK_DGRAM, 0);
  if(sd<0) {
    printf("%s: cannot open socket \n",argv[0]);
    exit(1);
  }

  /* bind local server port */
  servAddr.sin_family = AF_INET;
  servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servAddr.sin_port = htons(port);
  rc = bind (sd, (struct sockaddr *) &servAddr,sizeof(servAddr));
  if(rc<0) {
    printf("%s: cannot bind port number %d \n",
	   argv[0], port);
    exit(1);
  }

  for (i = 0; i < MAX_USERS; ++i) {
  		users[i].userName = NULL;
  	}

  printf("%s: waiting for data on port UDP %u\n",
	   argv[0],port);

/* BEGIN jcs 3/30/05 */

  flags = 0;

/* END jcs 3/30/05 */

  /* server infinite loop */
  while(1) {

    /* init buffer */
    memset(MsgBuff,0x0,MAX_MSG_LEN);

    /* receive message */
    cliLen = sizeof(cliAddr);
    n = recvfrom(sd, MsgBuff, MAX_MSG_LEN, flags,
		 (struct sockaddr *) &cliAddr
		 , &cliLen);

    if(n<0) {
      printf("%s: cannot receive data \n",argv[0]);
      continue;
    }

    /* print received message */
    printf("%s: from %s:UDP%u : %s \n",
	   argv[0],inet_ntoa(cliAddr.sin_addr),
	   ntohs(cliAddr.sin_port),MsgBuff);

/* BEGIN jcs 3/30/05 */

    res = executeCommand(MsgBuff);
//    sleep(1);
    sendto(sd,res,strlen(res),flags,(struct sockaddr *)&cliAddr,cliLen);

    printf("users:\n");
    for (i = 0 ; i < MAX_USERS ; i++)
    {
    	printf("users[%d] = %s\n",i,safeString(users[i].userName));
    	if (users[i].userName)
    	for (j = 0; j < MAX_GROUPS; ++j)
    	{
    		if (users[i].groups[j])
			printf("\t\t%s\n",users[i].groups[j]);
		}
    }
    printf("groups:\n");
	for (j = 0; j < MAX_GROUPS; ++j)
	{
		if (activeGroups[j])
			printf("%s\n",activeGroups[j]);
	}

/* END jcs 3/30/05 */

  }/* end of server infinite loop */

return 0;

}
